package mvc;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import converter.ConverterFactory;

/**
 * @author LW
 * 参数注射器，负责把前台参数注射入对应的对象内部
 */
public class ParameterInjector {
	/**
	 * argument 的索引
	 */
	public final int 						index;
	public final Class<?> 					argumentType;
	public final String						argumentName;
	public final Map<String ,SetterInfo> 	settersMap = new HashMap<String ,SetterInfo>();
	
	//basic types and strings do not need injections
	public final boolean 					needInject;
	public final boolean					isArray;	
	
	ParameterInjector(int index,Class<?> argumentType,String argumentName){
		this.index = index;
		this.argumentType = argumentType;
		this.argumentName = argumentName;
		
		isArray = argumentType.isArray();
		
		if(argumentType.isPrimitive() || argumentType.equals(String.class) || isArray){
			needInject = false;
		} else {
			needInject = true;
		}
		
		initSetters();
	}
	
	/**
	 * @param instance
	 * @param value
	 * @param key
	 * 
	 * 将请求参数注射入action参数对象中
	 */
	public void inject(Object instance,String value,String key){
		if(instance == null || !argumentType.isInstance(instance)){ return;}
		
		try {
			SetterInfo s = settersMap.get(key);
			s.setter.invoke(instance, (new ConverterFactory()).convert(s.fieldType, value));
		} catch (Exception e) {	}
	}
	
	/**
	 * @return 请求参数访问参数注射器的key
	 */
	public Set<String> getKeys(){
		return settersMap.keySet();
	}
	
	/**
	 * 从action参数中分离出请求参数名和对象字段的对应关系
	 */
	void initSetters(){
		if(needInject){
			ConverterFactory converter = new ConverterFactory();
			Field[] fields = argumentType.getDeclaredFields();
			Method setter;
			String setterName;
			SetterInfo sInfo;
			for(Field f : fields){
				if(converter.canConvert(f.getType())){
					setterName = "set"+firstCharToUpperCase(f.getName());
					try {
						setter = argumentType.getMethod(setterName, f.getType());
						sInfo = new SetterInfo(setter,f.getType());
						settersMap.put(argumentName+"."+f.getName(), sInfo);
					} catch (NoSuchMethodException e) {	}
				}
			}
		} else {
			settersMap.put(argumentName, new SetterInfo(null,null));
		}
	}
	
	String firstCharToUpperCase(String s){
		if(s == "" || s == null) return s;
		
		return s.substring(0, 1).toUpperCase() + s.substring(1);
	}
}

class SetterInfo {
	public final Method 	setter;
	public final Class<?>	fieldType;
	
	SetterInfo(Method setter,Class<?> fieldType){
		this.setter = setter;
		this.fieldType = fieldType;
	}
}
